# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
# This source file is part of the Cangjie project, licensed under Apache-2.0
# with Runtime Library Exception.
#
# See https://cangjie-lang.cn/pages/LICENSE for license information.

from os import path
import random
from itertools import groupby

integer_types = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64']
float_types = ['Float16', 'Float32', 'Float64']
number_types = integer_types + float_types
extend_types = number_types + ['String', 'Rune', 'Bool']
types = number_types + ['Unit', 'Tuple', 'Array', 'Range', 'C', 'S']
default_value_map = {'Bool' : 'true', 'Unit' : '()', 'Array' : '[1]', 'Rune' : "'1'", 'String' : '"1"',
                     'Tuple' : '(1, 1)', 'Range' : '(1..2)', 'C' : 'C(1)', 'S' : 'S(1)'}
eq_ops = ['==', '!=']
logical_ops = ['||', '&&']
bool_ops = eq_ops + logical_ops
rel_ops = eq_ops + ['<', '<=', '>', '>=']
str_ops = rel_ops + ['+']
number_ops = str_ops + ['-', '*', '/']
integer_ops = number_ops + ['%', '&', '|', '^', '<<', '>>']
ops = integer_ops + logical_ops

def default_value(ty : str) -> str:
  if ty in integer_types: return '1' + ty[0].lower() + ty[ty.index('t')+1:]
  if 'Float' in ty: return '1.0f' + ty[5:]
  return default_value_map[ty]

custom_type_template = '''
%s {
    let x : Int64

    init(y : Int64) { x = y }

    operator func ==(other : %s) { x == other.x }
}
'''
decl_map = {'Array': '\ntype Array = Array<Int64>\n', 'Tuple': '\ntype Tuple = (Int64, Int64)\n',
            'Range': '\ntype Range = Range<Int64>\n', 'Function': '\ntype Function = (Int64) -> Int64\n',
            'C': custom_type_template % ('class C', 'C'), 'S': custom_type_template % ('struct S', 'S')}
def decl(ty : str) -> str:
  if ty in decl_map: return decl_map[ty]
  return ''

dir = path.dirname(path.realpath(__file__))
path = dir + '/test_' + path.basename(dir) + '_{}.cj'
template = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/*
  @Assertion:   10.2.3(18)  5. For a type T, if T already supports some of the above operators that can be overloaded
                by default, then implementing the operator function with the same signature for T by extension will
                result in a redefinition error. For example, a redefinition error is reported when overloading a
                supported operator with the same signature such as an arithmetic operator, bitwise operator or
                relational operator for a numeric type, or overloading a relational operator with the same signature
                for the Rune type, or overloading a same signatured logical, equal or unequal operator for the Bool
                type, etc. 
  @Description: %s
  @Mode: %s
  @Negative: %s
  @Structure: single
  @CompileWarning: ignore
  @Comment: Auto-generated by gen.py
*/
%s
main() {%s}
'''
extend_template = '''
extend %s {
%s
}
'''
op_template = '    operator func %s(%s) { }'
counter = 1

def write_counted(contents : str):
  global counter
  with open(path.format(str(counter).zfill(3)), 'w') as file:
    file.write(contents)
    counter += 1

def positive(t1, t2, op):
  if t1 == t2:
    if t1 == 'Bool' and op in bool_ops: return False
    if t1 == 'Rune' and op in rel_ops: return False
    if t1 == 'String' and op in str_ops: return False
    if t1 in float_types and op in number_ops: return False
    if t1 in integer_types and op in integer_ops: return False
  if t1 in integer_types and t2 in integer_types and op in ['<<', '>>']: return False
  if op == '**' and (t1, t2) in [('Int64', 'UInt64'), ('Float64', 'Int64'), ('Float64', 'Float64')]: return False
  if op == '-' and t2 == '' and t1 in number_types: return False
  if op == '!' and (t1 is 'Bool' or t1 in integer_types): return False
  return True

data_to_insert = [(t1, t2, op, positive(t1, t2, op)) for t1 in extend_types for t2 in types for op in ops]
negative_tests = [template % ('Checks that operator %s cannot be overloaded for types %s and %s.' % (op, t1, t2), 'compileonly', 'yes',
   extend_template % (t1, op_template % (op, 'x: ' + t2)), ' ') for t1, t2, op, b in data_to_insert if not b]
negative_tests += [template % ('Checks that operator %s cannot be overloaded for type %s' % (op, t), 'compileonly', 'yes',
   extend_template % (t, op_template % (op, '')), '') for op in '!-' for t in extend_types if not positive(t, '', op)]

random.seed(123)
limit = 100
signatures = random.sample([(t1, t2, op) for t1, t2, op, b in data_to_insert if b and op not in logical_ops], limit)
sig_dict = {t1: [] for t1 in extend_types}
[sig_dict[t1].append((t2, op)) for t1, t2, op in signatures]
positive_test = template % ('Checks some operators that can be overloaded for integral types', 'run', 'no',
  ''.join(decl(t) for t in types) +
  ''.join([extend_template % (t1, '\n\n'.join([op_template % (op, 'x: ' + t2) for (t2, op) in sig_dict[t1]] +
                                              [op_template % (op, '') for op in '!-' if positive(t1, '', op)]))
                                        for t1 in extend_types]),
  '\n    '.join(['%s %s %s' % (default_value(t1), op, default_value(t2)) for t1, t2, op in signatures] +
                ['%s%s' % (op, default_value(t)) for op in '!-' for t in extend_types if positive(t, '', op)]) + '\n')

write_counted(positive_test)
for test in negative_tests:
  write_counted(test)
